package lte

import (
	"fmt"
	"math"
	"math/cmplx"

	"gitee.com/thubcc/mobile/utils"
)

type csTabItem struct {
	sf, sym, idx int
}

// CSTab cell specific pilot table
type CSTab struct {
	tab [4][]csTabItem
}

//Hfreq calculate freq domain channel
func (c *CSTab) Hfreq(rxSymbols utils.Array3, r []complex128) {
	for i := range r {
		r[i] = 0. + 0.i
	}
	for _, v := range c.tab[0] {
		r[v.idx] += rxSymbols.D[v.sf][v.sym][v.idx]
	}

	for _, v := range c.tab[1] {
		r[v.idx] -= rxSymbols.D[v.sf][v.sym][v.idx]
	}
	for _, v := range c.tab[2] {
		r[v.idx] -= complex(0., 1.) * rxSymbols.D[v.sf][v.sym][v.idx]
	}
	for _, v := range c.tab[3] {
		r[v.idx] += complex(0., 1.) * rxSymbols.D[v.sf][v.sym][v.idx]
	}

}

// H calculate time domain channel
func (c *CSTab) H(rxSymbols utils.Array3) []complex128 {
	ifft, err := fd2td128.Get()
	if err != nil {
		return nil
	}
	defer fd2td128.Return(ifft)
	c.Hfreq(rxSymbols, ifft.Buf)
	ifft.Execute()
	r := make([]complex128, 128)
	copy(r, ifft.Buf)
	return r
}

//Dump dump table
func (c *CSTab) Dump() {
	for i := 0; i < 4; i++ {
		fmt.Printf("========%d==========\n", i)
		for _, v := range c.tab[i] {
			fmt.Printf("(%d %d %d) ", v.sf, v.sym, v.idx)
		}
		fmt.Println()
	}
}

// LazyRefTabs cell specific pilot table, lazy generated
type LazyRefTabs struct {
	ref map[int][]*CSTab
}

//NewLazyTab init lazy table
func NewLazyTab() *LazyRefTabs {
	return &LazyRefTabs{
		make(map[int][]*CSTab),
	}
}

func (l *LazyRefTabs) Get(cid, txPort int) *CSTab {
	if r, ok := l.ref[cid]; ok {
		return r[txPort]
	} else {
		r = buildCSTab(cid, 4, 6, "normal")
		l.ref[cid] = r
		return r[txPort]
	}
}

func toIdx(c complex128) int {
	t := []complex128{complex(1.0, -1.0), complex(-1., 1.), complex(1., 1.), complex(-1., -1.)}
	for i, v := range t {
		if cmplx.Abs(c-v/math.Sqrt2) < 0.5 {
			return i
		}
	}
	return 3
}

func remap(idx int) int {
	if idx >= 36 {
		return idx + 1 - 36
	}
	return 128 + idx - 36
}

//BuildCSTab generate Cell specific pilot tab
func buildCSTab(cid, nofPorts, nofPrb int, cp string) []*CSTab {
	crs := NewCRS(cid, nofPorts, nofPrb, cp)
	r := []*CSTab{}
	for p := 0; p < nofPorts; p++ {
		tab := &CSTab{}
		for sf := 0; sf < LTE_NOF_SF_X_FRAME; sf++ {
			for l := 0; l < refsignal_cs_nof_symbols(p); l++ {
				nsymbol := refsignal_cs_nsymbol(l, crs.CP, p)
				fidx := (refsignal_cs_v(p, l) + (crs.ID % 6)) % 6
				for i := 0; i < 2*crs.NofPrb; i++ {
					pidx := LTE_REFSIGNAL_PILOT_IDX(i, l, crs.NofPrb)
					idx := toIdx(crs.Pilots[p/2][sf%10][pidx])
					tab.tab[idx] = append(tab.tab[idx], csTabItem{sf, nsymbol, remap(fidx)})
					fidx += LTE_NRE / 2
				}
			}
		}
		r = append(r, tab)
	}
	return r
}

var (
	lazyTable = NewLazyTab()
)

//GetCRSTable get cell specific pilot table
func GetCRSTable(cid, port int) *CSTab {
	return lazyTable.Get(cid, port)
}
